package xin.nick.common.entity.http;

import cn.hutool.core.util.StrUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import xin.nick.common.util.IoUtils;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * @author Nick
 */
public class RequestWrapper extends HttpServletRequestWrapper {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    private final byte[] streamBody;
    private static final int BUFFER_SIZE = 4096;

    public RequestWrapper(HttpServletRequest request) {
        super(request);
        byte[] bytes = null;
        try {
            bytes = inputStream2ByteArray(request.getInputStream());
        } catch (IOException e) {
            logger.error(e.getMessage(), e);
        }
        streamBody = bytes;
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        ByteArrayInputStream inputStream = new ByteArrayInputStream(streamBody);
        return new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener listener) {
            }

            @Override
            public int read() throws IOException {
                return inputStream.read();
            }
        };
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(this.getInputStream()));
    }


    private byte[] inputStream2ByteArray(InputStream inputStream) throws IOException {
        if (inputStream != null) {
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            byte[] bytes = new byte[BUFFER_SIZE];
            int length;
            while ((length = inputStream.read(bytes, 0, BUFFER_SIZE)) != -1) {
                outputStream.write(bytes, 0, length);
            }
            return outputStream.toByteArray();
        }
        return null;
    }

    @Override
    public Map<String, String[]> getParameterMap() {
        String encoding = super.getCharacterEncoding();
        Map<String, String[]> readOnlyParams = super.getParameterMap();
        Map<String, String[]> params = new HashMap<String, String[]>(readOnlyParams);

        Set<Map.Entry<String, String[]>> set = params.entrySet();
        try {
            for (Map.Entry<String, String[]> entry : set) {
                String key = URLDecoder.decode(entry.getKey(), encoding);
                String[] values = entry.getValue();
                for (int i = 0; i < values.length; i++) {
                    values[i] = URLDecoder.decode(values[i], encoding);
                }
                if (!key.equals(entry.getKey())) {
                    params.remove(entry.getKey());
                }
                params.put(key, values);
            }
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("URLDecoder异常", e);
        }

        String contentType = super.getContentType();
        if (contentType != null && contentType.toLowerCase().indexOf(MediaType.APPLICATION_FORM_URLENCODED.getSubtype()) >= 0) {
            try {
                String content = IoUtils.toString(this.getInputStream());
                if (StrUtil.isNotEmpty(content)) {

                    String[] array = content.split("&");
                    for (String item : array) {
                        String[] param = item.split("=");
                        String key = param[0];

                        if (params.containsKey(key)) {
                            if (param.length > 1) {
                                String[] values = params.get(param[0]);
                                String[] newValues = new String[values.length + 1];
                                for (int i = 0; i < values.length; i++) {
                                    newValues[i] = values[i];
                                }
                                newValues[values.length] = param[1];
                                params.put(key, newValues);
                            }
                        } else {
                            if (param.length == 1) {
                                params.put(key, new String[0]);
                            } else {
                                params.put(key, new String[]{param[1]});
                            }
                        }
                    }
                }

            } catch (IOException e) {
                logger.error(e.getMessage(), e);
            }
        }
        return params;
    }

}
